package com.lll.simple.rpc.consumer;

import com.lll.simple.rpc.common.*;
import com.lll.simple.rpc.protocol.MsgType;
import com.lll.simple.rpc.protocol.ProtocolConstants;
import com.lll.simple.rpc.protocol.serialization.SerializationTypeEnum;
import com.lll.simple.rpc.registry.RegistryService;
import io.netty.channel.DefaultEventLoop;
import io.netty.util.concurrent.DefaultPromise;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @Author: liull
 * @Description: 消费端调用核心所在，包含底层网络通信，负载均衡，服务发现等
 * @Date: Created in 16:41 2021/2/3
 * @Modified By:
 */
public class RpcInvokeProxy implements InvocationHandler {
    private final String serviceVersion;

    private final Long timeout;

    private final RegistryService registryService;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 1.构造rpc 请求协议
        RpcProtocol<RpcRequest> request = new RpcProtocol<>();
        // 1.1协议header
        MessageHeader header = new MessageHeader();
        long requestId = RpcRequestHolder.REQUEST_ID.incrementAndGet();
        header.setRequestId(requestId);
        header.setMagic(ProtocolConstants.MAGIC);
        header.setSerialization((byte) SerializationTypeEnum.HESSIAN.getType());
        header.setVersion(ProtocolConstants.VERSION);
        header.setMsgType((byte) MsgType.REQUEST.getType());
        header.setStatus((byte) 0x1);
        request.setHeader(header);
        // 1.2协议body
        RpcRequest body = new RpcRequest();
        body.setClassName(method.getDeclaringClass().getName());
        body.setMethodName(method.getName());
        body.setServiceVersion(serviceVersion);
        body.setParams(args);
        body.setParameterTypes(method.getParameterTypes());
        request.setBody(body);
        // 2.发送请求
        RpcConsumer rpcConsumer = new RpcConsumer();
        RpcFuture<RpcResponse> future = new RpcFuture<>(new DefaultPromise<>(new DefaultEventLoop()), timeout);
        RpcRequestHolder.REQUEST_MAP.put(requestId, future);
        rpcConsumer.sendRequest(request, this.registryService);
        return future.getPromise().get(future.getTimeout(), TimeUnit.MILLISECONDS).getData();
    }

    public RpcInvokeProxy(String serviceVersion, Long timeout, RegistryService registryService) {
        this.serviceVersion = serviceVersion;
        this.timeout = timeout;
        this.registryService = registryService;
    }
}
